All files / model document_set.ts

91.18% Statements 62/68
81.25% Branches 13/16
90% Functions 18/20
90.63% Lines 58/64
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158                                2x   2x     2x                 2x         2x 430x                     8217x 998x 37089x   7219x 11x     8217x 8217x     2x 621x     2x 13308x     2x 2x     2x 83x     2x 568x             2x 1860x 1860x     2x 645x       2x 1595x 7569x 7569x         2x   5963x 5963x             2x 7215x 7215x 6009x     1206x     2x 67x 66x   62x 62x 62x 82x 82x 82x   58x     2x                       2x       7169x 7169x 7169x 7169x 7169x   2x  
/**
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
import { SortedMap } from '../util/sorted_map';
 
import { documentMap } from './collections';
import { Document } from './document';
import { DocumentComparator } from './document_comparator';
import { DocumentKey } from './document_key';
 
/**
 * DocumentSet is an immutable (copy-on-write) collection that holds documents
 * in order specified by the provided comparator. We always add a document key
 * comparator on top of what is provided to guarantee document equality based on
 * the key.
 */
 
export class DocumentSet {
  /**
   * Returns an empty copy of the existing DocumentSet, using the same
   * comparator.
   */
  static emptySet(oldSet: DocumentSet): DocumentSet {
    return new DocumentSet(oldSet.comparator);
  }
 
  private comparator: DocumentComparator;
  private keyedMap: SortedMap<DocumentKey, Document>;
  private sortedSet: SortedMap<Document, null>;
 
  /** The default ordering is by key if the comparator is omitted */
  constructor(comp?: DocumentComparator) {
    // We are adding document key comparator to the end as it's the only
    // guaranteed unique property of a document.
    if (comp) {
      this.comparator = (d1: Document, d2: Document) =>
        comp(d1, d2) || DocumentKey.comparator(d1.key, d2.key);
    } else {
      this.comparator = (d1: Document, d2: Document) =>
        DocumentKey.comparator(d1.key, d2.key);
    }
 
    this.keyedMap = documentMap();
    this.sortedSet = new SortedMap<Document, null>(this.comparator);
  }
 
  has(key: DocumentKey): boolean {
    return this.keyedMap.get(key) != null;
  }
 
  get(key: DocumentKey): Document | null {
    return this.keyedMap.get(key);
  }
 
  first(): Document | null {
    return this.sortedSet.minKey();
  }
 
  last(): Document | null {
    return this.sortedSet.maxKey();
  }
 
  isEmpty(): boolean {
    return this.sortedSet.isEmpty();
  }
 
  /**
   * Returns the index of the provided key in the document set, or -1 if the
   * document key is not present in the set;
   */
  indexOf(key: DocumentKey): number {
    const doc = this.keyedMap.get(key);
    return doc ? this.sortedSet.indexOf(doc) : -1;
  }
 
  get size(): number {
    return this.sortedSet.size;
  }
 
  /** Iterates documents in order defined by "comparator" */
  forEach(cb: (doc: Document) => void): void {
    this.sortedSet.inorderTraversal((k, v) => {
      cb(k);
      return false;
    });
  }
 
  /** Inserts or updates a document with the same key */
  add(doc: Document): DocumentSet {
    // First remove the element if we have it.
    const set = this.delete(doc.key);
    return set.copy(
      set.keyedMap.insert(doc.key, doc),
      set.sortedSet.insert(doc, null)
    );
  }
 
  /** Deletes a document with a given key */
  delete(key: DocumentKey): DocumentSet {
    const doc = this.get(key);
    if (!doc) {
      return this;
    }
 
    return this.copy(this.keyedMap.remove(key), this.sortedSet.remove(doc));
  }
 
  isEqual(other: DocumentSet | null | undefined): boolean {
    if (!(other instanceof DocumentSet)) return false;
    if (this.size !== other.size) return false;
 
    const thisIt = this.sortedSet.getIterator();
    const otherIt = other.sortedSet.getIterator();
    while (thisIt.hasNext()) {
      const thisDoc = thisIt.getNext().key;
      const otherDoc = otherIt.getNext().key;
      if (!thisDoc.isEqual(otherDoc)) return false;
    }
    return true;
  }
 
  toString(): string {
    const docStrings: string[] = [];
    this.forEach(doc => {
      docStrings.push(doc.toString());
    });
    if (docStrings.length === 0) {
      return 'DocumentSet ()';
    } else {
      return 'DocumentSet (\n  ' + docStrings.join('  \n') + '\n)';
    }
  }
 
  private copy(
    keyedMap: SortedMap<DocumentKey, Document>,
    sortedSet: SortedMap<Document, null>
  ): DocumentSet {
    const newSet = new DocumentSet();
    newSet.comparator = this.comparator;
    newSet.keyedMap = keyedMap;
    newSet.sortedSet = sortedSet;
    return newSet;
  }
}